package com.zxy.ziems.server.device.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.zxy.btp.common.response.PageResult;
import com.zxy.ziems.server.budget.pojo.dto.BudgetValDTO;
import com.zxy.ziems.server.budget.pojo.entity.BudgetDefEntity;
import com.zxy.ziems.server.budget.pojo.entity.BudgetValueEntity;
import com.zxy.ziems.server.classify.pojo.entity.ClassifyDeviceProperty;
import com.zxy.ziems.server.classify.service.ClassifyDevicePropertyService;
import com.zxy.ziems.server.classify.service.ClassifyService;
import com.zxy.ziems.server.device.convertor.DeviceCustomDataConvertor;
import com.zxy.ziems.server.device.mapper.DeviceCustomDataMapper;
import com.zxy.ziems.server.device.pojo.dto.DeviceCustomDataDTO;
import com.zxy.ziems.server.device.pojo.dto.DeviceCustomDataValDTO;
import com.zxy.ziems.server.device.pojo.entity.DeviceCustomDataEntity;
import com.zxy.ziems.server.device.pojo.entity.DevicePropertyEntity;
import com.zxy.ziems.server.device.pojo.param.DeviceCustomDataParam;
import com.zxy.ziems.server.device.pojo.vo.DeviceCustomDataVO;
import com.zxy.ziems.server.device.service.DeviceCustomDataService;
import com.zxy.ziems.server.device.service.DevicePropertyService;
import com.zxy.ziems.server.energy.convertor.DeviceMeasureDataConvertor;
import com.zxy.ziems.server.energy.mapper.DeviceMeasureDataDayMapper;
import com.zxy.ziems.server.energy.pojo.entity.DeviceMeasureDataDayEntity;
import com.zxy.ziems.server.energy.service.EnergyPriceService;
import com.zxy.ziems.server.log.DataRecordTypeEnum;
import com.zxy.ziems.server.log.pojo.entity.DataRecordLogEntity;
import com.zxy.ziems.server.log.service.DataRecordService;
import com.zxy.ziems.server.mybatis.core.service.ServiceImplX;
import com.zxy.ziems.server.utils.AssertUtil;
import com.zxy.ziems.server.utils.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;

import static com.zxy.ziems.server.utils.DateUtils.YMD_PATTERN;

@Service
public class DeviceCustomDataServiceImpl extends ServiceImplX<DeviceCustomDataMapper, DeviceCustomDataEntity> implements DeviceCustomDataService {

    @Autowired
    private ClassifyDevicePropertyService classifyDevicePropertyService;
    @Autowired
    private ClassifyService classifyService;
    @Autowired
    private DeviceMeasureDataDayMapper deviceMeasureDataDayMapper;
    @Autowired
    private EnergyPriceService energyPriceService;
    @Autowired
    private DevicePropertyService devicePropertyService;
    @Autowired
    private DataRecordService dataRecordService;
    @Override
    public PageResult<DeviceCustomDataVO> page(DeviceCustomDataParam param) {
        PageResult<DeviceCustomDataVO> res = customPage(param, (page, param1) -> baseMapper.page(page, param1));
        List<DeviceCustomDataVO> list = res.getList();
        List<String> dateDayList = DateUtils.getDateDayList(param.getStartDate(), param.getEndDate(), YMD_PATTERN);
        if (CollectionUtil.isNotEmpty(list)) {
            Set<String> deviceIdSet = new HashSet<>();
            Set<String> dataTypeIdSet = new HashSet<>();
            list.forEach(m -> {
                m.setDayList(dateDayList);
                deviceIdSet.add(m.getDeviceId());
                dataTypeIdSet.add(m.getDataTypeId());
            });
            QueryWrapper<DeviceMeasureDataDayEntity> query = new QueryWrapper<>();
            query.lambda().in(DeviceMeasureDataDayEntity::getDeviceId, deviceIdSet)
                    .in(DeviceMeasureDataDayEntity::getDataTypeId, dataTypeIdSet)
                    .between(DeviceMeasureDataDayEntity::getMeasureTime, param.getStartDate(), param.getEndDate());
            List<DeviceMeasureDataDayEntity> deviceMeasureDataDayEntities = deviceMeasureDataDayMapper.selectList(query);
            if (CollectionUtil.isEmpty(deviceMeasureDataDayEntities)) return res;
            Map<String, List<DeviceMeasureDataDayEntity>> deviceTypeMap = deviceMeasureDataDayEntities.stream().collect(Collectors.groupingBy(m -> m.getDeviceId() + "-" + m.getDataTypeId()));
            list.forEach(m -> {
                List<DeviceMeasureDataDayEntity> entityList = deviceTypeMap.get(m.getDeviceId() + "-" + m.getDataTypeId());
                if (CollectionUtil.isNotEmpty(entityList)) {
                    Map<String, DeviceMeasureDataDayEntity> collect = entityList.stream().collect(Collectors.toMap(k -> DateUtils.dateToString(k.getMeasureTime(), YMD_PATTERN), v -> v, (e, e1) -> e));
                    Map<String, DeviceCustomDataValDTO> val = collect.entrySet().stream().filter(e -> dateDayList.contains(e.getKey())).collect(Collectors.toMap(k -> k.getKey(), v -> DeviceMeasureDataConvertor.INSTANT.dayEntity2DeviceCustomDataValDTO(v.getValue())));
                    m.setDeviceCustomDataVal(val);
                }
            });
        }
        return res;
    }

    @Transactional
    @Override
    public void add(DeviceCustomDataDTO dto) {
        DeviceCustomDataEntity entity = DeviceCustomDataConvertor.INSTANT.dto2Entity(dto);

        doCheckBeforeSaveAction(dto, entity);

        save(entity);
    }

    @Transactional
    @Override
    public void adds(List<DeviceCustomDataDTO> dtos) {
        for (DeviceCustomDataDTO dto : dtos) {
            add(dto);
        }
    }

    private void doCheckBeforeSaveAction(DeviceCustomDataDTO dto, DeviceCustomDataEntity entity) {
        AssertUtil.nonNull(classifyService.getById(dto.getClassifyId()), "未找到科室分类");
        List<ClassifyDeviceProperty> classifyDeviceProperties = classifyDevicePropertyService.getByClassId(dto.getClassifyId());
        AssertUtil.isTrue(CollectionUtil.isNotEmpty(classifyDeviceProperties), "请确认分类，或配置设备");
        AssertUtil.isTrue(classifyDeviceProperties.size() == 1, "请确认分类，或配置设备");

        ClassifyDeviceProperty classifyDeviceProperty = classifyDeviceProperties.get(0);
        QueryWrapper<DeviceCustomDataEntity> query = new QueryWrapper<>();
        query.lambda().eq(DeviceCustomDataEntity::getClassifyId, dto.getClassifyId())
                .eq(DeviceCustomDataEntity::getDeviceId, classifyDeviceProperty.getDeviceId())
                .eq(DeviceCustomDataEntity::getDataTypeId, classifyDeviceProperty.getDataTypeId());
        AssertUtil.isFalse(baseMapper.selectCount(query) > 0, "标签已存在，不可重复添加");
        entity.setDeviceId(classifyDeviceProperty.getDeviceId());
        entity.setDataTypeId(classifyDeviceProperty.getDataTypeId());
    }

    @Transactional
    @Override
    public void addVal(DeviceCustomDataDTO dto) {
        List<DeviceMeasureDataDayEntity> dayEntities = doCheckAndSetAddData(dto);

        addLog(dto);
        deviceMeasureDataDayMapper.batchSaveEntityList(dayEntities);
    }

    private void addLog(DeviceCustomDataDTO dto) {
        List<DataRecordLogEntity> logs = new ArrayList<>();
        DeviceCustomDataEntity entity = baseMapper.selectById(dto.getId());
        List<DeviceCustomDataValDTO> deviceCustomDataValDTOList = dto.getDeviceCustomDataValDTOList();
        Set<LocalDate> dateSet = deviceCustomDataValDTOList.stream().map(DeviceCustomDataValDTO::getMeasureTime).collect(Collectors.toSet());
        Map<LocalDate, BigDecimal> newData = deviceCustomDataValDTOList.stream().collect(Collectors.toMap(DeviceCustomDataValDTO::getMeasureTime, DeviceCustomDataValDTO::getIncrementValue));
        QueryWrapper<DeviceMeasureDataDayEntity> query = new QueryWrapper<>();
        query.lambda().eq(DeviceMeasureDataDayEntity::getDeviceId, entity.getDeviceId())
                .eq(DeviceMeasureDataDayEntity::getDataTypeId, entity.getDataTypeId())
                .in(DeviceMeasureDataDayEntity::getMeasureTime, dateSet);
        List<DeviceMeasureDataDayEntity> deviceMeasureDataDayEntities = deviceMeasureDataDayMapper.selectList(query);
        Map<LocalDate, BigDecimal> oldMap = deviceMeasureDataDayEntities.stream().collect(Collectors.toMap(DeviceMeasureDataDayEntity::getMeasureTime, DeviceMeasureDataDayEntity::getIncrementValue));
        newData.forEach((k, v) -> {
            DataRecordLogEntity log = new DataRecordLogEntity();
            BigDecimal oldVal = oldMap.get(k);
            log.setTag(DateUtils.dateToString(k, YMD_PATTERN));
            if (oldVal == null) {
                log.setNewData(v.toString());
                logs.add(log);
            } else if (oldVal.compareTo(v) != 0){
                log.setOldData(oldVal.toString());
                log.setNewData(v.toString());
                logs.add(log);
            }
        });
        dataRecordService.add(DataRecordTypeEnum.DEVICE_CUSTOM_DATA_MODIFY, entity.getTag(), entity.getId(), logs);
    }

    private List<DeviceMeasureDataDayEntity> doCheckAndSetAddData(DeviceCustomDataDTO dto) {
        List<DeviceMeasureDataDayEntity> res = new ArrayList<>();
        DeviceCustomDataEntity entity = getById(dto.getId());
        AssertUtil.nonNull(entity, "未找到录入项");

        DevicePropertyEntity deviceProperty = devicePropertyService.getBy(entity.getDeviceId(), entity.getDataTypeId());

        List<DeviceCustomDataValDTO> deviceCustomDataValDTOList = dto.getDeviceCustomDataValDTOList();

        for (DeviceCustomDataValDTO deviceCustomDataValDTO : deviceCustomDataValDTOList) {
            BigDecimal price = energyPriceService.getEnergyPriceByDataTypeId(entity.getDataTypeId(), deviceCustomDataValDTO.getMeasureTime().atStartOfDay());
            AssertUtil.nonNull(price, "能源类型未配置单价，请配置单价后添加");
            DeviceMeasureDataDayEntity e = DeviceMeasureDataConvertor.INSTANT.DeviceCustomDataVal2DayEntity(deviceCustomDataValDTO);
            e.setUnitId(deviceProperty.getUnitId());
            e.setDeviceId(entity.getDeviceId());
            e.setDataTypeId(entity.getDataTypeId());
            e.setIncrementCost(price.multiply(e.getIncrementValue()));
            res.add(e);
        }

        return res;
    }

    @Transactional
    @Override
    public void removeVal(DeviceCustomDataDTO dto) {
        List<String> ids = dto.getIds();
        for (String id : ids) {
            // 删除数据
            DeviceCustomDataEntity entity = getById(id);
            AssertUtil.nonNull(entity, "未找到删除数据");

            QueryWrapper<DeviceMeasureDataDayEntity> param = new QueryWrapper<>();
            param.lambda().eq(DeviceMeasureDataDayEntity::getDataTypeId, entity.getDataTypeId())
                    .eq(DeviceMeasureDataDayEntity::getDeviceId, entity.getDeviceId())
                    .isNull(DeviceMeasureDataDayEntity::getMeasureValue)
                    .between(DeviceMeasureDataDayEntity::getMeasureTime, dto.getStartDate(), dto.getEndDate());
            deviceMeasureDataDayMapper.delete(param);
        }
    }
}
